package cn.com.anypay.manager.service.auth.strategy;

import cn.com.anypay.manager.miaoma.sysuser.SysUserEntity;
import cn.com.anypay.manager.miaoma.sysuser.SysUserService;
import cn.com.anypay.manager.miaoma.sysuserauth.SysUserAuthEntity;
import cn.com.anypay.manager.miaoma.sysuserauth.SysUserAuthService;
import cn.com.anypay.manager.miaoma.sysuserauthlog.SysUserAuthLogService;
import cn.com.anypay.manager.service.auth.model.AuthRequest;
import cn.com.anypay.manager.service.auth.model.AuthResult;
import cn.dev33.satoken.stp.StpUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;

import jakarta.servlet.http.HttpServletRequest;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.Map;

/**
 * 抽象认证策略基类
 * 提供通用的认证流程模板和公共方法
 */
@Slf4j
public abstract class AbstractAuthStrategy implements IAuthStrategy {

    @Autowired
    protected SysUserService sysUserService;

    @Autowired
    protected SysUserAuthService sysUserAuthService;

    @Autowired
    protected SysUserAuthLogService sysUserAuthLogService;

    /**
     * 账户锁定时间（分钟）
     */
    protected static final int LOCK_DURATION_MINUTES = 30;

    /**
     * 通用认证流程模板方法
     * 定义了认证的标准流程，子类只需实现具体的认证逻辑
     */
    @Override
    public final AuthResult authenticate(AuthRequest request, HttpServletRequest servletRequest) {
        log.info("开始执行{}认证: identifier={}", getAuthType(), request.getIdentifier());

        try {
            // 1. 参数验证
            validateRequest(request);

            // 2. 查找或创建用户
            SysUserEntity user = findOrCreateUser(request, servletRequest);
            if (user == null) {
                return handleAuthFailure(null, request, servletRequest, "用户不存在");
            }

            // 3. 执行具体认证逻辑
            boolean authSuccess = doAuthenticate(request, user, servletRequest);

            if (authSuccess) {
                // 4. 认证成功处理
                return handleAuthSuccess(user, request, servletRequest);
            } else {
                // 5. 认证失败处理
                return handleAuthFailure(user, request, servletRequest, "认证失败");
            }

        } catch (Exception e) {
            log.error("{}认证异常: identifier={}", getAuthType(), request.getIdentifier(), e);
            sysUserAuthLogService.recordLoginFailure(null, getAuthType(), request.getIdentifier(),
                "认证过程发生异常: " + e.getMessage(), servletRequest);
            return AuthResult.failure("认证失败，请稍后重试");
        }
    }

    /**
     * 子类实现具体的认证逻辑
     *
     * @param request 认证请求
     * @param user 用户信息
     * @param servletRequest HTTP请求
     * @return 认证是否成功
     */
    protected abstract boolean doAuthenticate(AuthRequest request, SysUserEntity user,
                                            HttpServletRequest servletRequest);

    /**
     * 子类实现用户查找或创建逻辑
     *
     * @param request 认证请求
     * @param servletRequest HTTP请求
     * @return 用户信息
     */
    protected abstract SysUserEntity findOrCreateUser(AuthRequest request,
                                                     HttpServletRequest servletRequest);

    /**
     * 验证请求参数
     *
     * @param request 认证请求
     */
    protected void validateRequest(AuthRequest request) {
        if (request == null) {
            throw new IllegalArgumentException("认证请求不能为空");
        }
        if (request.getIdentifier() == null || request.getIdentifier().trim().isEmpty()) {
            throw new IllegalArgumentException("标识符不能为空");
        }
        if (request.getCredential() == null || request.getCredential().trim().isEmpty()) {
            throw new IllegalArgumentException("凭证不能为空");
        }
    }

    /**
     * 处理认证成功
     *
     * @param user 用户信息
     * @param request 认证请求
     * @param servletRequest HTTP请求
     * @return 认证结果
     */
    protected AuthResult handleAuthSuccess(SysUserEntity user, AuthRequest request,
                                         HttpServletRequest servletRequest) {
        try {
            // 1. 生成登录token
            StpUtil.login(user.getId());
            String token = StpUtil.getTokenValue();

            // 2. 更新认证记录
            SysUserAuthEntity authEntity = sysUserAuthService.getByAuthTypeAndKey(
                getAuthType(), request.getIdentifier());
            if (authEntity != null) {
                sysUserAuthService.incrementLoginCount(authEntity.getId());
            }

            // 3. 记录成功日志
            sysUserAuthLogService.recordLoginSuccess(user.getId(), getAuthType(), request.getIdentifier(),
                servletRequest);

            // 4. 构建返回数据
            Map<String, Object> data = new HashMap<>();
            data.put("token", token);
            data.put("userInfo", buildUserInfo(user));
            data.put("permissions", getUserPermissions(user));

            log.info("{}认证成功: userId={}, identifier={}", getAuthType(), user.getId(), request.getIdentifier());
            return AuthResult.success(user, data);

        } catch (Exception e) {
            log.error("处理认证成功时发生异常: userId={}, identifier={}", user.getId(), request.getIdentifier(), e);
            return AuthResult.failure("登录处理失败，请稍后重试");
        }
    }

    /**
     * 处理认证失败
     *
     * @param user 用户信息（可能为null）
     * @param request 认证请求
     * @param servletRequest HTTP请求
     * @param errorMessage 错误信息
     * @return 认证结果
     */
    protected AuthResult handleAuthFailure(SysUserEntity user, AuthRequest request,
                                         HttpServletRequest servletRequest, String errorMessage) {
        try {
            Long userId = user != null ? user.getId() : null;

            // 1. 更新认证记录（增加错误次数）
            SysUserAuthEntity authEntity = sysUserAuthService.getByAuthTypeAndKey(
                getAuthType(), request.getIdentifier());
            if (authEntity != null) {
                sysUserAuthService.incrementErrorCount(authEntity.getId());
            }

            // 2. 记录失败日志
            sysUserAuthLogService.recordLoginFailure(userId, getAuthType(), request.getIdentifier(),
                errorMessage, servletRequest);

            log.warn("{}认证失败: identifier={}, error={}", getAuthType(), request.getIdentifier(), errorMessage);
            return AuthResult.failure(errorMessage);

        } catch (Exception e) {
            log.error("处理认证失败时发生异常: identifier={}", request.getIdentifier(), e);
            return AuthResult.failure("认证失败");
        }
    }

    /**
     * 检查用户状态是否有效
     *
     * @param user 用户信息
     * @return 是否有效
     */
    protected boolean isUserActive(SysUserEntity user) {
        return user != null && user.getStatus() != null && user.getStatus() == 1;
    }

    /**
     * 检查认证实体是否有效
     *
     * @param authEntity 认证实体
     * @return 是否有效
     */
    protected boolean isAuthEntityValid(SysUserAuthEntity authEntity) {
        if (authEntity == null || authEntity.getStatus() == null || authEntity.getStatus() != 1) {
            return false;
        }

        // 检查是否过期
        if (authEntity.getExpireTime() != null && authEntity.getExpireTime().isBefore(LocalDateTime.now())) {
            return false;
        }

        return true;
    }

    /**
     * 检查账户是否被锁定
     *
     * @param authEntity 认证实体
     * @return 是否被锁定
     */
    protected boolean isAccountLocked(SysUserAuthEntity authEntity) {
        return authEntity != null && authEntity.getLockUntil() != null
            && authEntity.getLockUntil().isAfter(LocalDateTime.now());
    }

    /**
     * 获取用户权限信息
     * 子类可以重写此方法来提供特定的权限逻辑
     *
     * @param user 用户信息
     * @return 权限信息
     */
    protected Object getUserPermissions(SysUserEntity user) {
        // 默认实现，子类可以重写
        return null;
    }

    /**
     * 构建用户信息
     *
     * @param user 用户实体
     * @return 用户信息Map
     */
    protected Map<String, Object> buildUserInfo(SysUserEntity user) {
        Map<String, Object> userInfo = new HashMap<>();
        userInfo.put("id", user.getId());
        userInfo.put("username", user.getUsername());
        userInfo.put("realName", user.getRealName());
        userInfo.put("email", user.getEmail());
        userInfo.put("phone", user.getPhone());
        userInfo.put("avatar", user.getAvatar());
        return userInfo;
    }
}